﻿using System;
//using System.Windows.Forms;

namespace CMS.Web
{
	/// <summary>
	/// Class Encrypt cung cấp 2 hàm mã hoá và giải mã một chuỗi ký tự ASCII không quá 255 ký tự,
	/// bao gồm các ký tự hiển thị trên bàn phím ngoại trừ 3 ký tự đặc biệt là " \ và '
	/// </summary>
	public class Encrypt
	{
		/// <summary>
		/// Mã hoá chuỗi ký tự mã ASCII không quá 255 ký tự
		/// </summary>
		/// <param name="strEncrypt">Chuỗi cần mã hoá</param>
		/// <param name="strResult">Chuỗi nhận kết quả trả về sau khi mã hoá</param>
		/// <returns></returns>
		public static bool Encryption(string strEncrypt, ref string strResult)
		{
			strResult = strEncrypt;
			if (strEncrypt == null) return false;
			if( !((strEncrypt.Length > 0) && (strEncrypt.Length <= 255)) )//Độ dài không hợp lệ
				return false;//Mã hoá không thành công

			//Mảng gồm 90 ký tự hiển thị trên bàn phím (không có 5 ký tự đặc biệt " ' { } và \) dùng kiểm tra sự hợp lệ của chuỗi nhập vào
			string a = "0LSY9FashlND1:qxRM]ockJBCK8;UAbz=2e|wZEW*O@H7PQ#Xti%3yjT4$Ipnd6 +uVr)m~Gfv&g5`!^(_-[?/>.<,";

			for(int i = 0; i < strEncrypt.Length; i++)
				for(int j = 0; j < a.Length; j++)
				{
					if(strEncrypt[i] == a[j])
						break;
					else if(j == a.Length - 1)//Tồn tại ký tự không hợp lệ trong chuỗi cần mã hoá
						return false;//Mã hoá không thành công
				}

            string tmp = "";
			//---Mã hoá bằng giải thuật thay thế
			tmp = RepEncrypt(strEncrypt);

			//---Mã hoá bằng giải thuật ma trận
			strResult = MatrixEncrypt(tmp);

			return true;//Mã hoá thành công
		}
		/// <summary>
		/// Giải mã chuỗi ký tự mã ASCII không quá 255 ký tự
		/// </summary>
		/// <param name="strDeEncrypt">Chuỗi cần giải mã</param>
		/// <param name="strResult">Chuỗi nhận kết quả trả về sau khi giải mã</param>
		/// <returns></returns>
		public static bool DeEncryption(string strDeEncrypt, ref string strResult)
		{
			strResult = strDeEncrypt;
			if (strDeEncrypt == null) return false;
			if( !((strDeEncrypt.Length > 0) && (strDeEncrypt.Length <= 255)) )//Độ dài không hợp lệ
				return false;//Giải mã không thành công
			else
			{
				string tmp = "";
				
				//---Giải mã bằng giải thuật ma trận
				tmp	= MatrixDeEncrypt(strDeEncrypt);

				//---Giải mã bằng giải thuật thay thế
				strResult = RepDeEncrypt(tmp);

				return true;//Giải mã thành công
			}
		}
		/// <summary>
		/// Mã hoá chuỗi bằng giải thuật ma trận
		/// </summary>
		/// <param name="strInput">Là chuỗi ký tự ASCII gồm các ký tự hiển thị ngoại trừ 3 ký tự đặc biệt là \ " và '</param>
		/// <returns></returns>
		private static string MatrixEncrypt(string strInput)
		{
			//---Nếu độ dài chuỗi < 5 không áp dụng giải thuật này
			int lenght_strInput = strInput.Length;
			if(strInput.Length < 5)
				return strInput;

			string	strResult = "";
			string	strResult_tmp = "";
			int		col = lenght_strInput/3,
					i = 0,
					j = 0;

			//---Mã hoá theo giải thuật ma trận
			for(i = 0; i < col; i++)
			{
				j = 0;
				while( (j + i) < lenght_strInput )
				{
					strResult_tmp += strInput[j + i];
					j += col;
				}
			}

			//---Cải tiến giải thuật---//
			
			//---Xử lý trường hợp lỗi khi độ dài chuỗi chia 3 lấy phần nguyên lớn hơn 10
			//của vùng ký tự số trong phần cải tiến
			int iNum = 57;
			if(col > 10)
				iNum += col - 10; 
			
			//---Thực hiện cải tiến giải thuật
			int k = 0;
			while (k < lenght_strInput)
			{
				if( Char.IsNumber(strResult_tmp, k) )//Char.IsLetterOrDigit(strInput, p)
				{
					if( ( (Convert.ToByte(strResult_tmp[k]) + col) < 255) && ( Char.IsNumber( Convert.ToChar(Convert.ToByte(strResult_tmp[k]) + col) ) ) )
						strResult += Convert.ToChar( Convert.ToByte(strResult_tmp[k]) + col);
					else if(Char.IsDigit(strResult_tmp, k))//Ký tự số 47 - 57 vùng số
						strResult += Convert.ToChar(47 + col - (iNum - Convert.ToByte(strResult_tmp[k]) ) );
					else if(Char.IsLower(strResult_tmp, k))//Ký tự chữ in thường 96 - 122
						strResult += Convert.ToChar(96 + col - (122 - Convert.ToByte(strResult_tmp[k]) ) );
					else//Ký tự chữ in hoa 64 - 90
						strResult += Convert.ToChar(64 + col - (90 - Convert.ToByte(strResult_tmp[k]) ) );
				}
				else//Ký tự đặc biệt
					strResult += strResult_tmp[k];

				k++;
			}
			//---Kết quả trả về
			return strResult;
		}
		/// <summary>
		/// Giải mã chuỗi bằng giải thuật ma trận
		/// </summary>
		/// <param name="strInput">Là chuỗi ký tự ASCII gồm các ký tự hiển thị ngoại trừ 3 ký tự đặc biệt là \ " và '</param>
		/// <returns></returns>
		private static string MatrixDeEncrypt(string strInput)
		{
			//---Nếu độ dài chuỗi < 5 không áp dụng giải thuật này
			int lenght_strInput = strInput.Length;
			if(lenght_strInput < 5)
				return strInput;

			//------------------------------------//
			string	strResult = "";
			string	strResult_tmp = "";

			int		i = 0;
			int		n = 0;
			int		t = 0;
			int		h = lenght_strInput % (lenght_strInput / 3);
			
			int		Col = lenght_strInput / (lenght_strInput / 3);
			int		Cols = 0;
			int		Colss =  lenght_strInput / 3;
			int p = 0;

			//---Xử lý trường hợp lỗi khi độ dài chuỗi chia 3 lấy phần nguyên lớn hơn 10---//
			int iNum = 57;
			if(Colss > 10)
				iNum += Colss - 10; 

			//---Giải mã phần cải tiến giải thuật
			while (p < lenght_strInput)
			{
				if( Char.IsNumber(strInput, p) )//Char.IsLetterOrDigit(strInput, p)
				{
					if( ( ( (Convert.ToByte(strInput[p]) - Colss) > 47) && ( Char.IsNumber( Convert.ToChar(Convert.ToByte(strInput[p]) - Colss) ) ) ) )//Vùng ký tự đặc biệt
						strResult_tmp += Convert.ToChar(Convert.ToByte(strInput[p]) - Colss);
					else if(Char.IsDigit(strInput[p]))//Vùng ký tự số
						strResult_tmp += Convert.ToChar( iNum - (Colss - ( Convert.ToByte(strInput[p]) - 47)) );
					else if(Char.IsLower(strInput[p]))//Vùng ký tự chữ in thường
						strResult_tmp += Convert.ToChar( 122 - (Colss - (Convert.ToByte(strInput[p])- 96)) );
					else//Vùng ký tự chữ in hoa
						strResult_tmp += Convert.ToChar( 90 - (Colss - (Convert.ToByte(strInput[p]) - 64)) );
				}
				else//Vùng ký tự đặc biẹt
					strResult_tmp += strInput[p];
				p++;
			}

			//---Giải mã theo giải thuật ma trận
			if(h != 0)
				Cols = Col + 1;
			else
				Cols = Col;

			for(i = 0; i < Cols; i++)
			{
				n = 0;
				t = h;
				while( (n+i < lenght_strInput) && (strResult.Length < lenght_strInput) )
				{
					strResult += strResult_tmp[n+i];
					if(t!=0)
					{
						n += Col + 1;
						t--;
					}
					else
						n += Col;

				}
			}
			//---Kết quả trả về
			return strResult;
		}
		/// <summary>
		/// Mã hoá chuỗi bằng giải thuật thay thế
		/// </summary>
		/// <param name="strInput">Là chuỗi ký tự ASCII gồm các ký tự hiển thị ngoại trừ 5 ký tự đặc biệt là \ " { }và '</param>
		/// <returns></returns>
		private static string RepEncrypt(string strInput) 
		{
			int length_strInput = strInput.Length; 
			if(length_strInput == 0)
				return  "";
	
			string strResult = "";

			//Mảng gồm 90 ký tự hiển thị trên bàn phím (không có 5 ký tự đặc biệt " ' { } và \) dùng kiểm tra sự hợp lệ của chuỗi nhập vào
			string a = "0LSY9FashlND1:qxRM]ockJBCK8;UAbz=2e|wZEW*O@H7PQ#Xti%3yjT4$Ipnd6 +uVr)m~Gfv&g5`!^(_-[?/>.<,";
			string b = "F;GXenty30H?IAvg/jbBi<C1&DEr%2m#cP!Q9Rs `@^()_[>.,kY*Z$lUzp~wJxT-=S|]f8KL+4hqM5N:OV6Wo7dua";
		
			int index = 0;
			for(int i = 0; i < length_strInput; i++)
			{
				index = 0;

				//Tìm vị trí tương ứng trong mảng a[]
				while((index < a.Length) &&( strInput[i] != a[index]))
					index++;

				//Cộng thêm một hoán vị chỉ số i đồng dư cho số phần tử của mảng ký tự
				index  = (index + i % a.Length) % a.Length;

				//Thay thế ký tự tương ứng trong mảng b[]
				strResult += b[index];
			}
			return strResult;
		}
		/// <summary>
		/// Giải mã chuỗi bằng giải thuật thay thế
		/// </summary>
		/// <param name="strInput">Là chuỗi ký tự ASCII gồm các ký tự hiển thị ngoại trừ 5 ký tự đặc biệt là \ " { }và '</param>
		/// <returns></returns>
		private static string RepDeEncrypt(string strInput)
		{
			int length_strInput = strInput.Length; 
			if(length_strInput == 0)
				return  "";
	
			string strResult = "";

			//Mảng gồm 90 ký tự hiển thị trên bàn phím (không có 5 ký tự đặc biệt " ' { } và \) dùng kiểm tra sự hợp lệ của chuỗi nhập vào
			string a = "0LSY9FashlND1:qxRM]ockJBCK8;UAbz=2e|wZEW*O@H7PQ#Xti%3yjT4$Ipnd6 +uVr)m~Gfv&g5`!^(_-[?/>.<,";
			string b = "F;GXenty30H?IAvg/jbBi<C1&DEr%2m#cP!Q9Rs `@^()_[>.,kY*Z$lUzp~wJxT-=S|]f8KL+4hqM5N:OV6Wo7dua";
		
			int index = 0;
			for(int i = 0; i < length_strInput; i++)
			{
				index = 0;

				//Tìm vị trí tương ứng trong mảng b[]
				while((index < b.Length)&&(strInput[i] != b[index]))
					index++;
				
				//Tính lại vị trí đúng cho ký tự vì đã cộng đồng dư
				if( index < (i % b.Length) )
					index = index + b.Length - (i % b.Length);
				else
					index = index - (i % b.Length);
				
				//Thay thế ký tự tương ứng trong mảng a[]
				strResult += a[index];
			}
			return strResult;

			//Chuỗi đặc biệt để Test
			//.,mnbvcxzasdfghjkl;][poiuytewq`1234567890-=~!@#$%^&*()_+|POIUYTREWQASDFGHJKL:?><ertret34637fgt63^%$!(#!@$)__--+:;::::><?.<,>trg64eh457MNBVCX//.,mnbvcxzasdfghjkl;][poiuytewq`1234567890//.,mnbvcxzasdfghjkl;][poiuytewq`1234567890-=~!@#$%^&*zcvvzxvzv
		}
	}
}